package my.org.swing.date;

import my.org.swing.animate.AnimatePane;
import my.org.swing.animate.SideLayout;
import my.org.swing.animate.TranslateActor;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.time.LocalDate;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

class DateWindow extends JDialog {

    private static final long serialVersionUID = -920614069653573698L;

    private int windowWidth;
    private int windowHeight;

    private DateObserver dateField;
    private LocalDate calendar;
    private DateTable table;
    private int direction;

    private JButton btnTime;
    private JButton btnLast;
    private JButton btnNext;

    private String format = "%d年%d月";

    /**
     * 0 = 十年分页
     * 1 = 月份分页
     * 2 = 日历
     */
    private int mode = 2;

    /**
     * 鼠标滚动偏移量
     */
    private int rowOffset;

    private Handler handler = new Handler();

    /**
     * 主面板，mode切换时的动画面板
     */
    private AnimatePane mainPane;

    /**
     * 日期面板，按页或按行切换时不影响表头，需要独立于表头之外
     */
    private AnimatePane gridPane;

    /**
     * 包含表头部分的日历面板
     */
    private JPanel calendarPane;

    /**
     * @param dateField
     */
    public DateWindow(DateObserver dateField, LocalDate dateTime) {
        super(SwingUtilities.getWindowAncestor(dateField.getComponent()), ModalityType.MODELESS);
        setUndecorated(true);
        this.dateField = dateField;
        this.calendar = dateTime == null ? LocalDate.now() : dateTime;

        initStyle();
        initLayout();
        initListener();
        setResizable(false);
    }

    private void initStyle() {
        windowWidth = DateTheme.windowWidth;
        windowHeight = DateTheme.windowHeight;

        btnTime = new JButton();
        btnLast = new JButton(DateTheme.prePageText);
        btnNext = new JButton(DateTheme.nextPageText);

        int duration = DateTheme.animationTime;
        mainPane = new AnimatePane(duration);
        gridPane = new AnimatePane(duration);
    }

    private void initPaneColor(JPanel... panes) {
        for (JPanel c : panes) {
            c.setOpaque(true);
            c.setBackground(getThemeColor());
        }
    }

    private void initLayout() {
        gridPane.add(getTablePane());

        calendarPane = new JPanel(new BorderLayout());
        calendarPane.add(getWeekHeaderPane(), BorderLayout.NORTH);
        calendarPane.add(gridPane);

        mainPane.add(calendarPane);

        JPanel btnPane = new JPanel(new SideLayout());
        btnPane.add(btnTime, SideLayout.LEFT);
        btnPane.add(btnNext, SideLayout.RIGHT);
        btnPane.add(SideLayout.blankPane(2), SideLayout.RIGHT);
        btnPane.add(btnLast, SideLayout.RIGHT);
        btnPane.add(SideLayout.blankPane(5), SideLayout.RIGHT);

        initTopButton(btnTime, DateTheme.timeTextForeground);
        initTopButton(btnLast, DateTheme.prePageForeground);
        initTopButton(btnNext, DateTheme.nextPageForeground);

        JPanel root = new JPanel(new BorderLayout());
        root.add(btnPane, BorderLayout.NORTH);
        root.add(mainPane);
        root.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));

        add(root);
        setSize(windowWidth, windowHeight);
        initPaneColor(gridPane, calendarPane, mainPane, btnPane, root);
    }

    private void initTopButton(JButton btn, Color foreground) {
        btn.setForeground(foreground);
        btn.setBackground(getThemeColor());
        btn.setBorderPainted(false);
        btn.setFocusPainted(false);
        btn.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
        btn.setFont(DateTheme.otherPageCellFont);
    }

    private Color getThemeColor() {
        return DateTheme.backColor;
    }

    private JComponent getTablePane() {
        if (table != null) {
            table.clearListener();
            table.removeMouseWheelListener(handler);
        }

        setTimeInfo(calendar);
        table = new DateTable(mode, calendar);
        table.addMouseListener(handler);
        table.addMouseWheelListener(handler);

        JPanel pane = new JPanel(new BorderLayout());
        pane.add(table);
        initPaneColor(pane);

        if (mode < 2) {
            pane.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
        }

        return pane;
    }

    private void setTimeInfo(LocalDate time) {
        int year = time.getYear();
        if (mode == 0) {
            int offset = year % 10;
            btnTime.setText((year - offset) + " - " + (year - offset + 9));
        } else if (mode == 1) {
            btnTime.setText(year + "年");
        } else if (mode == 2) {
            btnTime.setText(String.format(format, year, time.getMonth().getValue()));
        }

        btnTime.setEnabled(mode > 0);
    }

    private JPanel getWeekHeaderPane() {
        JPanel pane = new JPanel(new GridLayout(1, 7));
        initPaneColor(pane);

        Dimension size = new Dimension(table.getCellSize(), table.getCellSize());
        Color color = getThemeColor();
        for (int i = 0; i < 7; i++) {
            JLabel cell = new JLabel(getWeekHeader(i));
            cell.setOpaque(true);
            cell.setPreferredSize(size);
            cell.setHorizontalAlignment(JLabel.CENTER);
            cell.setBackground(color);
            cell.setForeground(DateTheme.weekHeaderForeground);
            cell.setFont(DateTheme.weekHeaderFont);

            pane.add(cell);
        }
        return pane;
    }

    private String getWeekHeader(int index) {
        switch (index) {
            case 0:
                return "一";
            case 1:
                return "二";
            case 2:
                return "三";
            case 3:
                return "四";
            case 4:
                return "五";
            case 5:
                return "六";
            case 6:
                return "日";
            default:
                return null;
        }
    }

    private void initListener() {
        addWindowFocusListener(handler);

        btnTime.addActionListener(handler);
        btnLast.addActionListener(handler);
        btnNext.addActionListener(handler);
    }

    /**
     * 鼠标滚动换行
     */
    private void changeRow() {
        LocalDate time = table.changeOffset(rowOffset);
        setTimeInfo(time);
    }

    /**
     * 换页
     */
    private void changePage() {
        TranslateActor policy = new TranslateActor(direction < 0 ? TranslateActor.BottomMoving : TranslateActor.TopMoving);
        if (mode == 2) {
            calendar = calendar.plusMonths(direction);
            gridPane.setPolicy(policy);
            gridPane.show(getTablePane());
        } else {
            if (mode == 1) {
                calendar = calendar.plusYears(direction);
            } else {
                calendar = calendar.plusYears(direction * 10);
            }

            mainPane.setPolicy(policy);
            mainPane.show(getTablePane());
        }
    }

    /**
     * 切换内容
     */
    private void changeMode() {
        mainPane.setPolicy(new TranslateActor(direction < 0 ? TranslateActor.CenterShrink : TranslateActor.CenterExpand));
        if (mode == 2) {
            gridPane.setContent(getTablePane());

            calendarPane.setName("111");
            mainPane.show(calendarPane);
        } else if (mode == 1 || mode == 0) {
            mainPane.show(getTablePane());
        }

        table.requestFocusInWindow();
    }

    /**
     * show dialog.
     */
    void showMe(int x, int y) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        if (x + windowWidth > (int) screenSize.getWidth()) {
            x = (int) screenSize.getWidth() - windowWidth - 20;
        }

        if (y + windowHeight > (int) screenSize.getHeight()) {
            y = (int) screenSize.getHeight() - windowHeight - 20;
        }
        setLocation(x, y);
        setVisible(true);
    }

    class Handler extends MouseAdapter implements WindowFocusListener, ActionListener {

        @Override
        public void mousePressed(MouseEvent e) {
            if (e.getButton() == MouseEvent.BUTTON1) {
                if (mode == 1) {
                    int row = table.rowAtPoint(e.getPoint());
                    int col = table.columnAtPoint(e.getPoint());
                    CellValue value = (CellValue) table.getValueAt(row, col);
                    calendar = calendar.withYear(value.getYear()).withMonth(value.getMonth()).withDayOfMonth(1);

                    mode++;
                    direction = 1;
                    rowOffset = 0;
                    changeMode();
                } else if (mode == 0) {
                    int row = table.rowAtPoint(e.getPoint());
                    int col = table.columnAtPoint(e.getPoint());
                    CellValue value = (CellValue) table.getValueAt(row, col);
                    calendar = calendar.withYear(value.getYear()).withMonth(value.getMonth()).withDayOfMonth(1);

                    mode++;
                    direction = 1;
                    rowOffset = 0;
                    changeMode();
                }
            }
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            DateTable table = (DateTable) e.getSource();
            if (e.getButton() == MouseEvent.BUTTON1) {
                if (mode == 2 && dateField != null && e.getClickCount() >= 2) {
                    int row = table.rowAtPoint(e.getPoint());
                    int col = table.columnAtPoint(e.getPoint());
                    CellValue value = (CellValue) table.getValueAt(row, col);
                    dateField.dateChanged(value.toLocalDate());
                    rowOffset = 0;
                    dispose();
                }
            }
        }

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
            direction = e.getWheelRotation() > 0 ? 1 : -1;
            rowOffset += direction;
            changeRow();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Object source = e.getSource();
            rowOffset = 0;
            if (source == btnTime) {
                if (mode <= 0) {
                    return;
                }

                mode--;
                direction = -1;
                changeMode();
            } else if (source == btnLast) {
                direction = -1;
                changePage();
            } else if (source == btnNext) {
                direction = 1;
                changePage();
            }
        }

        @Override
        public void windowGainedFocus(WindowEvent e) {

        }

        @Override
        public void windowLostFocus(WindowEvent e) {
            if (mode != 2) {
                mode = 2;
                rowOffset = 0;
                calendar = LocalDate.now();
                changeMode();
            }

            dispose();
        }
    }
}
